#include "listview.h"

#include "dummyrow.h"
#include "itemrow.h"
#include "sourcerow.h"

#include "list.h"
#include "item.h"
#include "source.h"

#include "pricedelegate.h"
#include "quantitydelegate.h"

#include "urlutils.h"

#include <QStandardItemModel>
#include <QList>
#include <QHeaderView>
#include <QKeyEvent>

#include <assert.h>

ListView::ListView(QWidget *parent)
:   QTreeView(parent)
,   m_list(NULL)
,   m_model(NULL)
,   m_itemDummyRow(NULL)
,   m_priceDelegate(new PriceDelegate())
,   m_quantityDelegate(new QuantityDelegate())
{
    header()->setStretchLastSection(true);

    setSelectionMode(QAbstractItemView::ExtendedSelection);

    setItemDelegateForColumn(1, m_priceDelegate);
    setItemDelegateForColumn(2, m_quantityDelegate);

    connect(this, SIGNAL(doubleClicked(const QModelIndex &))
        , this, SLOT(me_activated(const QModelIndex &)));
}

ListView::~ListView()
{
    cleanUp();

    delete m_quantityDelegate;
}

void ListView::cleanUp()
{
    emit itemAppendable(false);

    delete m_itemDummyRow;
    m_itemDummyRow = NULL;

    assert(m_itemRows.isEmpty());

    delete m_model;
    m_model = NULL;
}

void ListView::setList(List *list)
{
    if(m_list == list)
        return;

    cleanUp();

    m_list = list;
    if(!m_list)
    {
        setModel(NULL);
        return;
    }

    setupNewModel();
    fillUp();

    connectToList(m_list);

    // setup view

    expandAll();
    resizeAllColumnsToContents();

    // emit status

    emit itemAppendable(true);
}

void ListView::setupNewModel()
{  
    m_model = new QStandardItemModel();
    m_model->setColumnCount(4);

    m_model->setHorizontalHeaderLabels(QStringList()
        << tr("Item Name / Source Name") << tr("Price") << tr("Quantity") << tr("Source URL"));

    // create dummy

    m_itemDummyRow = new DummyRow(
        tr("Append new Shopping List Item"), QIcon(":/item_put"));

    m_model->appendRow(m_itemDummyRow->items());

    // apply model

    setModel(m_model);

    connect(m_model, SIGNAL(itemChanged(QStandardItem*))
        , this, SLOT(me_itemChanged(QStandardItem*)));

    emit itemAppendable(true);
}

void ListView::fillUp()
{
    foreach(Item *item, m_list->items())
        list_itemAdded(item);
}

void ListView::connectToList(List *list)
{
    connect(list, SIGNAL(itemAdded(Item*))
        , this, SLOT(list_itemAdded(Item*)));

    connect(list, SIGNAL(itemRemoved(Item*))
        , this, SLOT(list_itemRemoved(Item*)));
}

ItemRow *ListView::itemRow(const QModelIndex &index) const
{
    QStandardItem *item = m_model->itemFromIndex(index);

    foreach(ItemRow *ir, m_itemRows)
        if(ir->hasItemInRow(item))
            return ir;

    return NULL;
}

ItemRow *ListView::itemByItemsDummyRow(const QModelIndex &index) const
{
    QStandardItem *item = m_model->itemFromIndex(index);

    foreach(ItemRow *ir, m_itemRows)
        if(ir->hasItemInDummyRow(item))
            return ir;

    return NULL;
}

ItemRow *ListView::itemRow(Item *item) const
{
    if(!item)
        return NULL;

    foreach(ItemRow *ir, m_itemRows)
        if(ir->item() == item)
            return ir;

    return NULL;
}

SourceRow *ListView::sourceRow(const QModelIndex &index) const
{
    QStandardItem *item = m_model->itemFromIndex(index);

    foreach(ItemRow *ir, m_itemRows)
    {
        SourceRow *sr(ir->sourceRow(item));
        if(sr)
            return sr;
    }
    return NULL;
}

SourceRow *ListView::sourceRow(Source *source) const
{
    foreach(ItemRow *ir, m_itemRows)
    {
        SourceRow *sr(ir->sourceRow(source));
        if(sr)
            return sr;
    }
    return NULL;
}

Item *ListView::currentItem()
{
    const QModelIndex current(currentIndex());

    ItemRow *ir(itemRow(current));
    SourceRow *sr(NULL);

    if(!ir)
        ir = itemByItemsDummyRow(current);
    if(!ir)
        sr = sourceRow(current);

    if(ir)
        return ir->item();
    if(sr)
        return sr->parent()->item();

    return NULL;
}

Source *ListView::currentSource()
{
    SourceRow *sr(sourceRow(currentIndex()));
    if(sr)
        return sr->source();

    return NULL;
}

void ListView::list_itemAdded(Item *item)
{
    assert(!itemRow(item));

    ItemRow *ir = new ItemRow(item);
    m_itemRows.insert(ir);

    // connect

    connect(ir, SIGNAL(columnWidthChanged(int))
        , this, SLOT(resizeColumnToContents(int)));

    connect(ir, SIGNAL(multipleColumnWidthsChanged())
        , this, SLOT(resizeAllColumnsToContents()));

    connect(ir, SIGNAL(dummyStatusChanged())
        , this, SLOT(checkIfWebSourceIsAppendable()));

    connect(ir, SIGNAL(select(Item*))
        , this, SLOT(select(Item*)));
    connect(ir, SIGNAL(select(Source*))
        , this, SLOT(select(Source*)));

    // insert

    m_model->insertRow(m_itemDummyRow->row(), ir->items());

    select(item);

    expand(ir->firstColumnItem()->index());
    resizeAllColumnsToContents();
}

void ListView::removeItem(const QModelIndex &index)
{
    ItemRow *ir = itemRow(index);
    if(!ir)
        return;

    m_list->removeItem(ir->item());

    // deletion is done on signal itemRemoved
}

void ListView::list_itemRemoved(Item *item)
{
    ItemRow *ir = itemRow(item);

    m_itemRows.remove(ir);

    const int row = ir->row();
    m_model->takeRow(row);

    delete ir;

    select(currentItem());

    resizeAllColumnsToContents();
}

void ListView::createItem()
{
    Item *item = new Item(m_list->nameSpace(), tr("Named Item"));
    m_list->addItem(item);
}

void ListView::createSource(const bool tryFromClipboard)
{
    const QModelIndex index(currentIndex());

    ItemRow *ir(itemRow(index));
    if(!ir)
        ir = itemByItemsDummyRow(index);
    if(!ir)
    {
        SourceRow *sr(sourceRow(index));
        if(!sr)
            return;

        ir = sr->parent();
    }
    createSource(ir->firstColumnItem()->index(), tryFromClipboard);
}

void ListView::createSource(
    const QModelIndex &index
,   const bool tryFromClipboard)
{
    ItemRow *ir(itemRow(index));

    if(!ir)
        ir = itemByItemsDummyRow(index);

    assert(ir);

    // create source

    Source *source(NULL);
    if(tryFromClipboard && ir->validAndUnknownUrlInClipboard())
    {
        const QUrl url(urlFromClipboard());
        source = new Source(
            ir->item()->nameSpace()
        ,   Source::host(url)
        ,   INVALID_PRICE
        ,   url);
    }
    else
    {
        source = new Source(
            ir->item()->nameSpace()
        ,   tr("Named Source"));
    }

    Item *item(ir->item());
    item->addSource(source);

    // if succeeds: signal sourceCreated in  row->item() should be emitted
}

void ListView::deleteSource(const QModelIndex &index)
{
    SourceRow *sr(sourceRow(index));
    if(sr)
    {
        assert(sr->parent()->item());
        sr->parent()->item()->removeSource(sr->source());
    }
}

void ListView::deleteItem(const QModelIndex &index)
{
    assert(m_model);

    ItemRow *ir = itemRow(index);
    if(ir)
    {
        removeItem(index);
        return;
    }
    deleteSource(index);
}

void ListView::select(Item *item)
{
    ItemRow *ir = itemRow(item);
    if(!ir)
        return;

    QItemSelection is(ir->items().first()->index(), ir->items().last()->index());
    selectionModel()->select(is, QItemSelectionModel::ClearAndSelect);

    setCurrentIndex(ir->firstColumnItem()->index());
}

void ListView::select(Source *source)
{
    if(!source)
    {
        select(currentItem());
        return;
    }

    SourceRow *sr = sourceRow(source);
    if(!sr)
        return;

    QItemSelection is(sr->items().first()->index(), sr->items().last()->index());
    selectionModel()->select(is, QItemSelectionModel::ClearAndSelect);

    setCurrentIndex(sr->firstColumnItem()->index());
}

// Interaction Handling

void ListView::currentChanged(
    const QModelIndex &current
,   const QModelIndex &previous)
{
    ItemRow *ir(itemRow(current));
    if(!ir)
        ir = itemByItemsDummyRow(current);

    SourceRow *sr(NULL);

    if(!ir && (sr = sourceRow(current)))
        ir = sr->parent();

    if(!ir)
        ir = itemByItemsDummyRow(current);

    emit itemRemovable(ir);

    emit webSourceAppendable((sr && sr->parent()->validAndUnknownUrlInClipboard())
        || (ir && ir->validAndUnknownUrlInClipboard()));
    emit userSourceAppendable(sr || ir);
    emit sourceRemovable(sr);    
}

void ListView::me_activated(const QModelIndex &index)
{
    QStandardItem *item = m_model->itemFromIndex(index);

    if(m_itemDummyRow->hasItemInRow(item))
    {
        createItem();
        return;
    }

    foreach(ItemRow *ir, m_itemRows)
    {
        if(ir->hasItemInDummyRow(item))
        {
            createSource(index);
            return;
        }
    }
}

void ListView::me_itemChanged(QStandardItem *item)
{
    ItemRow *ir(itemRow(item->index()));
    if(ir)
    {
        ir->itemChanged(item);
        return;
    }

    SourceRow *sr(sourceRow(item->index()));
    if(sr)
    {
        sr->itemChanged(item);
        return;
    }
}

void ListView::keyPressEvent(QKeyEvent *event)
{
    if(event->key() == Qt::Key_Delete)
    {
        event->accept();
        deleteItem(currentIndex());
    }
    event->ignore();
}

void ListView::checkIfWebSourceIsAppendable()
{
    const QModelIndex current(currentIndex());

    ItemRow *ir(itemRow(current));
    SourceRow *sr(NULL);

    if(!ir && (sr = sourceRow(current)))
        ir = sr->parent();

    if(!ir)
        ir = itemByItemsDummyRow(current);

    emit webSourceAppendable(ir
        && ir->validAndUnknownUrlInClipboard());
}

void ListView::resizeAllColumnsToContents()
{
    for(int i = 0; i < m_model->columnCount(); ++i)
        resizeColumnToContents(i);
}
